11 工业级 UI 开发

工业级 UI 开发

关联:索引

问题探究:

字段 含义 示例 UI 展示建议
deviceId 设备编号 SORT-01 可复制,作为主键
deviceName 设备名称 一号分拣机 表格主列
line 产线/区域 A 线 用于筛选与分组
station 工位/站点 入料口-1 次级信息
health 健康状态 ok/warn/alarm/offline 用 Tag 显示(颜色统一)
alarmLevel 告警等级 none/L1/L2/L3 与 health 联动显示

3) TypeScript 类型建议(便于 AI 生成一致代码)

export type Health = 'ok' | 'warn' | 'alarm' | 'offline'
export type AlarmLevel = 'none' | 'L1' | 'L2' | 'L3'
 
export interface DeviceRow {
  deviceId: string
  deviceName: string
  line: string
  station: string
  health: Health
  alarmLevel: AlarmLevel
  heartbeatAt: number
  throughputPerMin: number
  rejectRate: string
  lastErrorCode?: string
  updatedAt: string
}

export interface DeviceQueryForm {
  deviceName: string
  health: Health | ''
  alarmLevel: AlarmLevel | ''
  line: string
  station: string
}

作业:

布置

1)完成一个完整业务页面(仪表盘 / 设备列表二选一)。

2)实现响应式布局、表格、筛选、分页。

3)保证界面整洁、交互正常、符合工业系统风格。


1. 你必须记住的 3 个边界

前置条件

已具备:Vue3 + Vite + TypeScript 初始化项目。

安装依赖:

npm i element-plus @element-plus/icons-vue

步骤 1:在 main.ts 集成 Element Plus(含国际化)

示例(来自 09_element_plus_demo/src/main.ts,Element Plus 中文 + 图标 + Pinia/Router):

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { createPinia } from 'pinia'
 
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
 
const app = createApp(App)
 
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}
 
app.use(ElementPlus, {
  locale: zhCn,
})
app.use(createPinia())
app.use(router)
 
app.mount('#app')

步骤 2:搭建后台页面骨架(Layout)

示例结构(来自 09_element_plus_demo/src/App.vue,包含菜单路由联动与图标):

<template>
  <el-container style="height: 100vh; width: 100%">
    <el-aside width="220px" style="background-color: #2f4050">
      <div class="logo">智能分拣系统</div>
      <el-menu
        :default-active="activeMenu"
        mode="vertical"
        background-color="#2f4050"
        text-color="#fff"
        active-text-color="#ffd04b"
        router
      >
        <el-menu-item index="/dashboard">
          <el-icon><House /></el-icon>
          <span>仪表盘</span>
        </el-menu-item>
        <el-menu-item index="/device-list">
          <el-icon><Box /></el-icon>
          <span>设备列表</span>
        </el-menu-item>
      </el-menu>
    </el-aside>
    <el-container>
      <el-header style="text-align: right; font-size: 12px; background: #fff; border-bottom: 1px solid #e6e6e6; line-height: 60px; padding: 0 20px">
        管理员
      </el-header>
      <el-main style="background: #f3f3f4; padding: 20px; overflow: auto">
        <router-view />
      </el-main>
    </el-container>
  </el-container>
</template>

<script setup lang="ts">
import { computed } from 'vue'
import { useRoute } from 'vue-router'
import { House, Box } from '@element-plus/icons-vue'

const route = useRoute()
const activeMenu = computed(() => route.path)
</script>

步骤 3:创建“设备列表”页面骨架(筛选 + 表格 + 分页)

  1. 查询区(Form inline)
  2. 表格区(Table)
  3. 分页区(Pagination)
  4. 弹窗区(Dialog:新增/编辑/详情)

页面结构建议(用于 AI 生成的需求描述):

常见错误排查(当堂必讲)

规则:条件变化 → 回到第一页;提交成功 → 关闭弹窗并刷新列表;关闭弹窗 → 重置表单与校验状态。

项目工坊主题:实现智能分拣系统仪表盘、设备列表、筛选、分页、操作栏。

A. 仪表盘(Dashboard)建议实现点

示例代码(来自 09_element_plus_demo/src/views/DashboardView.vue,最近告警表格 + Tag 映射):

import type { AlarmLevel } from '../types/device'

const getAlarmTagType = (level: AlarmLevel) => {
  const map = {
    none: 'info',
    L1: 'warning',
    L2: 'danger',
    L3: 'danger',
  }
  return map[level]
}

最近告警表(字段建议):

B. 设备列表(Device List)必做闭环

示例代码(来自 09_element_plus_demo/src/views/DeviceListView.vue,建议学生对照复现并再抽取复用):

  1. 状态 Tag 映射(统一文案与颜色):
import type { Health, AlarmLevel } from '../types/device'

const getHealthTagType = (health: Health) => {
  const map = { ok: 'success', warn: 'warning', alarm: 'danger', offline: 'info' }
  return map[health]
}

const getHealthText = (health: Health) => {
  const map = { ok: '正常', warn: '警告', alarm: '告警', offline: '离线' }
  return map[health]
}

const getAlarmTagType = (level: AlarmLevel) => {
  const map = { none: 'info', L1: 'warning', L2: 'danger', L3: 'danger' }
  return map[level]
}
  1. 离线判定(状态与操作联动的前置口径):
import type { DeviceRow } from '../types/device'

const updateOfflineStatus = (list: DeviceRow[]): DeviceRow[] => {
  const now = Date.now()
  return list.map(item => ({
    ...item,
    health: now - item.heartbeatAt > 180000 ? 'offline' : item.health,
  }))
}
  1. 筛选 + 分页闭环(筛选/重置回到第 1 页;分页保留筛选条件):
import { ref, reactive } from 'vue'
import type { DeviceRow, DeviceQueryForm } from '../types/device'

const queryForm = reactive<DeviceQueryForm>({
  deviceName: '',
  health: '',
  alarmLevel: '',
  line: '',
  station: '',
})

const pageInfo = reactive({ page: 1, pageSize: 10, total: 0 })
const originDeviceList = ref<DeviceRow[]>([])
const deviceList = ref<DeviceRow[]>([])

const filterDeviceList = () => {
  let data = updateOfflineStatus([...originDeviceList.value])
  if (queryForm.deviceName) data = data.filter(i => i.deviceName.includes(queryForm.deviceName))
  if (queryForm.health) data = data.filter(i => i.health === queryForm.health)
  if (queryForm.alarmLevel) data = data.filter(i => i.alarmLevel === queryForm.alarmLevel)
  if (queryForm.line) data = data.filter(i => i.line.includes(queryForm.line))
  if (queryForm.station) data = data.filter(i => i.station.includes(queryForm.station))

  pageInfo.total = data.length
  const start = (pageInfo.page - 1) * pageInfo.pageSize
  deviceList.value = data.slice(start, start + pageInfo.pageSize)
}

const handleQuery = () => { pageInfo.page = 1; filterDeviceList() }
const resetQuery = () => {
  Object.assign(queryForm, { deviceName: '', health: '', alarmLevel: '', line: '', station: '' })
  pageInfo.page = 1
  filterDeviceList()
}
const handleSizeChange = () => { pageInfo.page = 1; filterDeviceList() }
const handleCurrentChange = () => { filterDeviceList() }
  1. 弹窗表单校验(关闭清校验;保存后刷新列表):
import { ref, reactive } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'

const dialogVisible = ref(false)
const deviceFormRef = ref<FormInstance>()
const deviceFormRules = reactive<FormRules>({
  deviceName: [{ required: true, message: '请输入设备名称', trigger: 'blur' }],
  line: [{ required: true, message: '请输入产线', trigger: 'blur' }],
  station: [{ required: true, message: '请输入工位', trigger: 'blur' }],
  health: [{ required: true, message: '请选择运行状态', trigger: 'change' }],
  alarmLevel: [{ required: true, message: '请选择告警等级', trigger: 'change' }],
})

const handleDialogClose = () => { deviceFormRef.value?.clearValidate() }
const submitDeviceForm = async () => {
  await deviceFormRef.value?.validate()
  dialogVisible.value = false
  getDeviceList()
}
  1. 筛选表单(Form inline)
  1. 表格(Table)
  1. 分页(Pagination)
  1. 弹窗(Dialog + Form 校验)
  1. 先做纯展示:表格渲染 mock 数据 + 基础分页展示 total
  2. 再做查询闭环:筛选表单 → 点击查询 → 重新计算/拉取 rows,并把 page 归 1
  3. 再做分页闭环:切页/改 pageSize 时仍带着筛选条件刷新 rows
  4. 最后做弹窗闭环:新增/编辑 → 校验 → 保存 → 关闭弹窗 → 刷新列表

把下面提示词直接发给 AI,要求输出“文件路径 + 完整代码”,生成后按审计清单自查。

提示词(设备列表页闭环):

请基于 Vue3 + Vite + TypeScript + Element Plus 技术栈,为智能分拣系统后台开发完整页面。请严格按照指定文件路径输出可直接运行的完整代码,必须包含完整 import、组件引用、类型定义、交互逻辑,禁止使用代码片段、省略号或不完整写法。
需输出文件(按顺序)
src/main.ts
全局集成 Element Plus 并引入样式,正确配置 Element Plus 中文 locale;注册 Element Plus 图标;集成 Pinia 与 Router;完成应用创建与挂载。
src/App.vue
实现标准后台布局:el-container / el-aside / el-header / el-main;
左侧菜单包含:仪表盘、设备列表;
主区域渲染路由页面;
必须使用 <script setup lang="ts">。
src/router/index.ts
创建 router(history 使用 createWebHistory(import.meta.env.BASE_URL)),导出默认 router。
src/router/routes.static.ts
定义静态路由:/ 重定向到 /dashboard;/dashboard;/device-list;未知路由重定向到 /dashboard。
src/types/device.ts
输出 Health/AlarmLevel/DeviceRow/DeviceQueryForm 四个类型定义(与下方严格一致),供页面复用。
src/views/DashboardView.vue
页面结构:4 个统计指标卡片 + 1 个图表占位区域 + 1 个最近告警表格;
允许使用 mock 数据;全部使用 Element Plus 组件实现。
src/views/DeviceListView.vue
必须完整实现:
筛选表单:deviceName /health/alarmLevel /line/station
设备表格渲染
分页与列表数据联动
操作栏:查看 / 编辑 / 设备启停 / 告警确认
新增 / 编辑共用弹窗表单,内置表单校验
强制约束
仅使用 Element Plus,不引入任何其他 UI 库
统一使用 <script setup lang="ts">,禁止使用 any 类型
严格遵循以下 TypeScript 类型定义(不可修改):
```ts
type Health = 'ok' | 'warn' | 'alarm' | 'offline'
type AlarmLevel = 'none' | 'L1' | 'L2' | 'L3'
interface DeviceRow {
 deviceId: string
 deviceName: string
 line: string
 station: string
 health: Health
 alarmLevel: AlarmLevel
 heartbeatAt: number
 throughputPerMin: number
 rejectRate: string
 lastErrorCode?: string
 updatedAt: string
}
interface DeviceQueryForm {
 deviceName: string
 health: Health | ''
 alarmLevel: AlarmLevel | ''
 line: string
 station: string
}
```
离线判断规则:当前时间戳(ms)- heartbeatAt(ms)> 180000(即 180 秒)→ 强制视为 offline,状态 Tag 与操作按钮必须联动
交互规则:
筛选 / 重置后自动回到第 1 页
保存成功后关闭弹窗并刷新列表
关闭弹窗时清空表单校验状态
输出格式
所有文件按 文件路径 + 完整代码块 输出,代码可直接复制运行;
最后提供 6 条标准化自查清单,包含:样式展示、中文国际化、筛选功能、分页联动、表单校验、危险操作确认。

审计清单(至少自查 6 条):

加分项(可选):

项目工坊任务

  1. 完成智能分拣系统仪表盘或设备列表(至少一个达到可交付)
  2. 完成筛选 + 表格 + 分页 + 弹窗的交互闭环(设备列表优先)
  3. 完成响应式布局调优(窄屏不崩、信息仍可读)

1. 样式没生效(最常见)

2. 中文/国际化不生效

3. 表格布局抖动/难读

4. 筛选与分页联动混乱

5. 弹窗表单残留/校验不正确

附录

  1. Element-plus官方网址:https://element-plus.org/zh-CN/component/overview.